package com.infoservice.filestore.impl;

import java.io.InputStream;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Random;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import com.infoservice.filestore.FSInputStream;
import com.infoservice.filestore.FileStore;
import com.infoservice.filestore.FileStoreException;
import com.infoservice.filestore.config.NameNodeInfo;
import com.infoservice.filestore.utils.IdUtil2;

/**
 * @author KevinSun
 * 
 */
public class FileStoreImpl extends FileStore {
	static {
		System.setProperty("jcifs.smb.client.dfs.disabled", "true");
	}
	private static Random rand = new Random(System.currentTimeMillis());
	private static Logger logger = LogManager.getLogger(LocalDataNode.class);

	private HashMap<String, NameNode> idMap = new HashMap<String, NameNode>();
	private HashMap<String, NameNode> hashMap = new HashMap<String, NameNode>();
	private LinkedList<String> validNodes = new LinkedList<String>();

	public FileStoreImpl(HashMap<String, NameNodeInfo> nameNodeMap) throws FileStoreException {
		for (NameNodeInfo obj : nameNodeMap.values()) {
			NameNode nnode = new NameNodeImpl(obj);
			if (this.hashMap.containsKey(nnode.getHash()))
				throw new FileStoreException("duplicated hash !");
			this.idMap.put(obj.getId(), nnode);
			this.hashMap.put(nnode.getHash(), nnode);
			if (nnode.canWrite())
				this.validNodes.addLast(nnode.getHash());
		}
	}

	private NameNode getNameNodeById(String id) {
		return this.idMap.get(id);
	}

	private NameNode getNameNodeByHash(String hash) {
		return this.hashMap.get(hash);
	}

	private NameNode getRandomNameNode() {
		String key = this.validNodes.get(rand.nextInt(this.validNodes.size()));
		return this.hashMap.get(key);
	}

	private String[] retrieveFileId(String fileName) throws FileStoreException {
		if (fileName.length() < IdUtil2.ID_LEN) {
			throw new FileStoreException("文件长度小于32! " + fileName);
		}
		return IdUtil2.retrieve(fileName.substring(0, IdUtil2.ID_LEN));
	}

	// ================================================================

	public String write(String nameNodeId, String dataNodeId, String fileName, InputStream in) throws FileStoreException {
		NameNode nnode = null;
		DataNode dnode = null;

		if (fileName == null || fileName.length() == 0) {
			throw new FileStoreException("文件名不能为空");
		}

		if (nameNodeId != null)
			nnode = this.getNameNodeById(nameNodeId);
		else
			nnode = this.getRandomNameNode();

		if (nnode == null)
			throw new FileStoreException("not found name node :" + nameNodeId);

		if (dataNodeId != null)
			dnode = nnode.getDataNodeById(dataNodeId);
		else
			dnode = nnode.getRandomDataNode();

		if (dnode == null)
			throw new FileStoreException("找不到data node :" + nameNodeId + "->" + dataNodeId + " 或者该datanode不可写入");

		String fname = IdUtil2.genFileId(nnode.getHash(), dnode.getHash(), fileName);//fileName;//
		String[] tmp = IdUtil2.retrieve(fname);

		dnode.write("/" + tmp[2], fileName, in);//fname
		String result=getUrl(tmp,fileName);
		return result;
	}

	public byte[] read(String fileName) throws FileStoreException {
		String[] nodes = this.retrieveFileId(fileName);
		NameNode nnode = this.getNameNodeByHash(nodes[0]);
		if (nnode == null) {
			throw new FileStoreException(fileName + "文件找不到或该DataNode不可读取");
		}

		DataNode dnode = nnode.getDataNodeByHash(nodes[1]);
		if (dnode == null) {
			logger.debug("name node no exist! hash=" + nodes[0] + "  " + fileName);
			throw new FileStoreException(fileName + "文件找不到或该DataNode不可读取");
		}

		return dnode.read("/" + nodes[2], fileName);
	}

	public FSInputStream getInputStream(String fileName) throws FileStoreException {
		String[] nodes = this.retrieveFileId(fileName);

		NameNode nnode = this.getNameNodeByHash(nodes[0]);
		if (nnode == null) {
			logger.debug("name node no exist! hash=" + nodes[0] + "  " + fileName);
			throw new FileStoreException(fileName + "文件找不到或该DataNode不可读取");
		}

		DataNode dnode = nnode.getDataNodeByHash(nodes[1]);
		if (dnode == null) {
			throw new FileStoreException(fileName + "文件找不到或该DataNode不可读取");
		}

		return new FSInputStream(dnode.getInputStream("/" + nodes[2], fileName),4096,fileName);
	}

	public boolean delete(String fileName) throws FileStoreException {
		String[] nodes = this.retrieveFileId(fileName);

		NameNode nnode = this.getNameNodeByHash(nodes[0]);
		if (nnode == null) {
			logger.debug("name node no exist! hash=" + nodes[0] + "  " + fileName);
			throw new FileStoreException(fileName + "文件找不到或该DataNode不可删除");
		}

		DataNode dnode = nnode.getDataNodeByDeleteHash(nodes[1]);
		if (dnode == null) {
			throw new FileStoreException(fileName + "文件找不到或该DataNode不可删除");
		}

		return dnode.delete("/" + nodes[2], fileName);
	}

	public String getUrl(String[] nodes,String fileName) throws FileStoreException {
//		String[] nodes = this.retrieveFileId(fileName);

		NameNode nnode = this.getNameNodeByHash(nodes[0]);
		if (nnode == null) {
			logger.debug("name node no exist! hash=" + nodes[0] + "  " + fileName);
			throw new FileStoreException("invalide name node! " + fileName);
		}

		DataNode dnode = nnode.getDataNodeByHash(nodes[1]);
		if (dnode == null) {
			logger.debug("data node not exist! hash=" + nodes[1] + "  " + fileName);
			throw new FileStoreException("invalide data node! " + fileName);
		}

		StringBuilder sbd = new StringBuilder();
		sbd.append(nnode.getDomain());
		sbd.append("/" + dnode.getId());
		sbd.append("/" + nodes[2]);
		sbd.append("/" + fileName);
		return sbd.toString();
	}

	public String getDomainURL(String fileName) throws FileStoreException {
		String[] nodes = this.retrieveFileId(fileName);

		NameNode nnode = this.getNameNodeByHash(nodes[0]);
		if (nnode == null) {
			logger.debug("name node no exist! hash=" + nodes[0] + "  " + fileName);
			throw new FileStoreException("invalide name node! " + fileName);
		}

		DataNode dnode = nnode.getDataNodeByHash(nodes[1]);
		if (dnode == null) {
			logger.debug("data node not exist! hash=" + nodes[1] + "  " + fileName);
			throw new FileStoreException("invalide data node! " + fileName);
		}

		StringBuilder sbd = new StringBuilder();
		sbd.append(nnode.getDomain());
		sbd.append("/" + dnode.getId());
		sbd.append("/" + nodes[2]);
		sbd.append("/" + fileName);
		return sbd.toString();
	}

	public String copy(String fname, String nameNodeId, String dataNodeId) throws FileStoreException {
		InputStream in = null;
		try {
			in = this.getInputStream(fname);
			return this.write(nameNodeId, dataNodeId, fname, in);
		} catch (FileStoreException e) {
			throw e;
		} catch (Exception e) {
			throw new FileStoreException("copy file error! " + fname + " to " + nameNodeId + "->" + dataNodeId, e);
		} finally {
			try {
				if (in != null)
					in.close();
			} catch (Exception e) {
				throw new FileStoreException("copy file error! " + fname + " to " + nameNodeId + "->" + dataNodeId, e);
			}
		}
	}

}
